account: use dwshell with more than one DWService account - #9
Merged
Conversation
Choosing between DWService accounts per command, the way the AWS CLI chooses a profile, under two constraints that shape the whole design: someone who does not want the feature must never learn it exists, and an existing configuration keeps working. Hence the email as the key, the first account registered becoming the default silently, and a flat configuration migrated in memory on load rather than rewritten underneath a read-only command. @ is not available as a selector: dwshell alice@myserver already means the remote OS user. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG
Six tasks. The design keeps the multi-account logic inside internal/config, so the client changes from cfg.Session to cfg.Current().Session and little else, and the two constraints that motivated the feature are each pinned by a test rather than left as intentions: loading a flat configuration must not rewrite the file, and a single account must be usable whether or not it is marked default. The live check exploits a property of the two real accounts: the second owns the machines the first sees as shares, so a wrong selection shows up as own-versus-shared on the same machine instead of something subtle. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG
Log in twice with different emails and both are kept; choose between them with --account or DWSHELL_ACCOUNT. The email is the account's identity, so logging in again with a known one refreshes it, exactly as before. The feature is built to be invisible until it is wanted. With a single account there is no default to think about: Select uses the lone account whether or not it is marked default, no output changes, and the word appears only in `account list`. That is a test, not an intention. An existing configuration is migrated in memory on load and written in the new shape only when something saves for a reason of its own — a read-only command must not rewrite the user's file behind their back. Verified against a real configuration: after `list` the file still had its flat keys, and only a command that saves turned them into accounts. Removing the default promotes the survivor when exactly one remains and otherwise leaves it unset, refusing later commands with an explanation rather than silently pointing them at the wrong account. login refuses --account: which account it touches is decided by the email being logged in, and it deliberately does not select an account, since "several accounts and no default" is a state logging in resolves. Verified live with two real accounts, which see the same machines from opposite sides: Regia reads shared from one and own from the other, so a wrong selection would be obvious rather than subtle. Checked through the flag, the environment variable, the default, and back. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG
ale-rinaldi
force-pushed
the
feat/multi-account
branch
from
September 5, 2026 21:40
55b4f75 to
139ca69
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
docs/superpowers/specs/2026-09-05-multi-account-design.md.Log in twice with different emails and dwshell keeps both; choose between them with
--accountorDWSHELL_ACCOUNT. The email is the account's identity, so logging in again with a known one refreshes it exactly as before.The two requirements, as tests rather than intentions
Invisible until wanted. With a single account there is no default to think about:
Selectuses the lone account whether or not it is marked default, no output changes, and the word appears only inaccount list.TestSelectWithOneAccountAndNoDefaultpins it.An existing configuration keeps working. The flat shape is migrated in memory on load and written in the new shape only when something saves for a reason of its own — a read-only command must not rewrite the user's file behind their back.
TestLoadDoesNotRewriteTheFilepins that, and it was confirmed against a real configuration:session,trustedDevice,userdwshell listsession,trustedDevice,user— untouchedaccounts,defaultThe migration test builds its fixture from generated keys, not invented ones: the signing key is parsed on load, so a made-up JWK would fail for reasons unrelated to migration.
Decisions worth flagging
loginrefuses--account— which account it touches is decided by the email — and deliberately does not select one, since "several accounts, no default" is a state logging in resolves.@is not a selector:dwshell alice@myserveralready means the remote OS user.dwshell list --account …). This is pre-existing for every flag including--config; the shell shortcut still accepts them first (dwshell --account … myserver). Documented in the README.Verification
Live, with two real accounts that see the same machines from opposite sides — the second owns what the first sees as shares — so a wrong selection is obvious rather than subtle:
liston the defaultsharedlist --account info@…ownDWSHELL_ACCOUNT=info@…ownlistagain, no flagshared— the default is untouched-c, flag before and after the agentaccount default, then no flag--accountaccount rmwith no terminal and no--yesAll of it ran against scratch configurations built from real credentials; the working configuration was never modified, and the scratch copies were deleted afterwards.
gofmt,go vet,go test -race ./...green — 18 new tests ininternal/config.🤖 Generated with Claude Code
https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG